package com.wyz.note.project.auth.core.config.security;

import cn.hutool.core.bean.BeanUtil;
import com.wyz.note.project.auth.common.constant.AuthConstant;
import com.wyz.note.project.auth.common.enums.RedisKeyEnum;
import com.wyz.note.project.auth.common.utils.ContextUtil;
import com.wyz.note.project.auth.common.utils.JWTTokenUtil;
import com.wyz.note.project.auth.common.utils.RedisUtil;
import com.wyz.note.project.auth.core.exception.TokenException;
import com.wyz.note.project.auth.domain.service.UserAuthInfoService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description: Jwt 过滤器
 * @Author: wei yz
 * @Date: 2022/6/19 16:43
 */
public class JwtTokenFilter extends OncePerRequestFilter {
    private static final Logger log = LoggerFactory.getLogger(JwtTokenFilter.class);

    @Autowired
    private UserAuthInfoService userAuthInfoService;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String authorization = request.getHeader(AuthConstant.TOKEN_HEADER);
        log.info("==> 当前jwt过滤器token: 【Authorization: {}】", authorization);
        log.info("==> 当前请求的URL: {}】", request.getRequestURI());
        // 非 /login URL 则校验token
        if (StringUtils.isNotBlank(authorization)) {
            // 是否为“Bearer ” 开头的字符串
            if (!authorization.startsWith(AuthConstant.TOKEN_HEAD)) {
                throw new ServletException("请求参数缺少 [ Bearer ] ");
            }
            // 根据"Bearer "取出tokenKey
            String tokenKey = authorization.substring(AuthConstant.TOKEN_HEAD.length());
            // 根据tokenKey从redis获取token
            log.info("==> 前端传进来的tokenKey：{}", tokenKey);
            // 根据短ID拿出token
            String token = (String) redisUtil.get(RedisKeyEnum.AUTH_TOKEN.getKey() + tokenKey);
            // 为空则表示在redis中过时，
            log.info("==> 从缓存中拿到的token：{}", token);
            if (StringUtils.isNotBlank(token)) {
                try {
                    // 解析token中拿到用户名
                    String username = JWTTokenUtil.getSub(token);
                    log.info("==> 解析token，当前用户名: {}", username);
                    if (StringUtils.isNotBlank(username)) {
                        // 验证该用户,查询redis获取认证信息，通过认证创建UsernamePasswordAuthenticationToken
                        UserAuthInfoDTO userAuthInfoDTO = (UserAuthInfoDTO) redisUtil.get(RedisKeyEnum.USER_INFO_BASE.getKey() + tokenKey);
                        log.info("==> 原始Redis登录的用户信息: {}", userAuthInfoDTO);
                        UserAuthInfo userAuthInfo = new UserAuthInfo();
                        BeanUtils.copyProperties(userAuthInfoDTO, userAuthInfo);
                        UserDetails userDetails = userAuthInfo;
                        log.info("==> 转换Redis登录的用户信息: {}", userDetails);
                        if (BeanUtil.isEmpty(userDetails)) {
                            userDetails = this.userAuthInfoService.loadUserByUsername(username);
                            log.info("==> 数据库查询的登录用户信息: {}", userDetails);
                            if (BeanUtil.isEmpty(userDetails)) {
                                throw new TokenException("用户信息异常: " + username);
                            }
                        }
                        // 验证用户信息
                        if (username.equals(userDetails.getUsername())) {
                            log.info("==> 验证通过 <==");
                            //验证成功则创建认证token
                            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                    userDetails, null, userDetails.getAuthorities());
                            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                            log.info("==> 创建认证token信息: {}", authentication);
                            // 注入Security上下文
                            SecurityContextHolder.getContext().setAuthentication(authentication);
                            log.info("==> 过滤器注入Security上下文 <==");
                        }
                    }
                } catch (Exception e) {
                    throw new TokenException(e.getMessage());
                }
            }
        }
        chain.doFilter(request, response);
    }
}
